package com.yehebl.handler;

import com.esotericsoftware.asm.*;
import com.yehebl.handler.annotaction.Handler;
import com.yehebl.handler.annotaction.NotNull;
import com.yehebl.handler.manager.*;
import com.yehebl.handler.util.ScanUtil;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 入口
 * 
 * @author tzd
 *
 */
public class InitMain {
	public IGetInstance getInstance;

	public void loadConfig(HandlerUtil handlerUtil,String pack) {
		Set<Class<?>> classes = new HashSet<>();
		String[] split = pack.split(",");
		Map<Class<?>,String> pathMap=new HashMap<>();
		for (int i = 0; i < split.length; i++) {
			classes.addAll(ScanUtil.getClasses(split[i],pathMap));
		}
		for (Class<?> clazz : classes) {
			Handler handler = clazz.getAnnotation(Handler.class);
			if (handler == null) {
				continue;
			}
			Method[] methods = clazz.getMethods();
			MethodAccessManager.set(clazz);
			Object o = null;
			if (getInstance != null) {
				o = getInstance.get(clazz);
			} else {
				try {
					o = clazz.newInstance();
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			for (Method method : methods) {
				Handler handlerMethod = method.getAnnotation(Handler.class);
				if (handlerMethod == null) {
					continue;
				}
				NotNull notNull = method.getAnnotation(NotNull.class);
				String firstKey = "";

				if (!"".equals(handler.value())) {
					firstKey = handler.value();
				} else if (0x80000000 != handler.code()) {
					firstKey = handler.code() + "";
				}
				String tailKey = "";
				if (!"".equals(handlerMethod.value())) {
					tailKey = handlerMethod.value();
				} else if (0x80000000 != handlerMethod.code()) {
					tailKey = handlerMethod.code() + "";
				}
				String key = firstKey + tailKey;
				if (notNull != null) {
					handlerUtil.getNotNullManager().add(key, notNull);
				}
				handlerUtil.getInstanceManager().add(key, o);
				handlerUtil.getMethodManager().add(key, method);
				handlerUtil.getMethodParamNamesManager().add(key, getMethodParamNamesSpring(method,pathMap));
				handlerUtil.getParameterTypeManager().add(key, method.getParameterTypes());
				handlerUtil.getReturnTypeManager().add(key, method.getReturnType().getName());
			}
		}
	}

	private static String[] getMethodParamNamesSpring(final Method m,Map<Class<?>,String> pathMap) {
		ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
		return discoverer.getParameterNames(m);
	}


	private static String[] getMethodParamNames(final Method m,Map<Class<?>,String> pathMap) {

		final String[] paramNames = new String[m.getParameterTypes().length];
		ClassReader cr = null;
		try {
			cr = new ClassReader(new FileInputStream(pathMap.get(m.getDeclaringClass())));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

		cr.accept(new ClassVisitor(Opcodes.ASM5) {
			@Override
			public MethodVisitor visitMethod(final int access, final String name, final String desc,
											 final String signature, final String[] exceptions) {
				final Type[] args = Type.getArgumentTypes(desc);
				// 方法名相同并且参数个数相同
				if (!name.equals(m.getName()) || !sameType(args, m.getParameterTypes())) {
					return super.visitMethod(access, name, desc, signature, exceptions);
				}
				MethodVisitor v = super.visitMethod(access, name, desc, signature, exceptions);
				return new MethodVisitor(Opcodes.ASM5, v) {
					@Override
					public void visitLocalVariable(String name, String desc, String signature, Label start, Label end,
												   int index) {
						int i = index - 1;
						// 如果是静态方法，则第一就是参数
						// 如果不是静态方法，则第一个是"this"，然后才是方法的参数
						if (Modifier.isStatic(m.getModifiers())) {
							i = index;
						}
						if (i >= 0 && i < paramNames.length) {
							paramNames[i] = name;
						}
						super.visitLocalVariable(name, desc, signature, start, end, index);
					}

				};
			}
		}, 0);
		return paramNames;
	}

	private static boolean sameType(Type[] types, Class<?>[] clazzes) {
		// 个数不同
		if (types.length != clazzes.length) {
			return false;
		}

		for (int i = 0; i < types.length; i++) {
			if (!Type.getType(clazzes[i]).equals(types[i])) {
				return false;
			}
		}
		return true;
	}

	public  IGetInstance getGetInstance() {
		return getInstance;
	}

	public  void setGetInstance(IGetInstance getInstance) {
		this.getInstance = getInstance;
	}

}
